package com.campus.schedule.timer;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.campus.common.enums.OrderStatusEnum;
import com.campus.schedule.entity.DeliveryDailyEarnings;
import com.campus.schedule.entity.MerchantDailyEarnings;
import com.campus.schedule.entity.Order;
import com.campus.schedule.entity.PlatformDailyEarnings;
import com.campus.schedule.service.DeliveryDailyEarningsService;
import com.campus.schedule.service.MerchantDailyEarningsService;
import com.campus.schedule.service.OrderService;
import com.campus.schedule.service.PlatformDailyEarningsService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 统一收益结算定时任务
 * 同时处理平台、商户、配送员的收益统计
 * 
 * @author ZhaoYuJie
 * @since 2025-01-15
 */
@Slf4j
@Service
public class UnifiedEarningsSettlementService {

    @Autowired
    private OrderService orderService;
    
    @Autowired
    private PlatformDailyEarningsService platformDailyEarningsService;
    
    @Autowired
    private MerchantDailyEarningsService merchantDailyEarningsService;
    
    @Autowired
    private DeliveryDailyEarningsService deliveryDailyEarningsService;

    /**
     * 每天凌晨5点执行统一收益结算（应该在分账定时任务之后执行）
     * 统计今天之前所有已完成且已结算但未统计的订单，按完成时间分组生成收益汇总记录
     * 这样可以避免因定时任务中断导致的数据遗漏问题
     * 采用分批查询避免内存溢出
     */
    @Scheduled(cron = "0 0 5 * * ?")
    @Transactional(rollbackFor = Exception.class)
    public void settleAllEarnings() {
        log.info("开始执行统一收益结算任务");
        
        try {
            LocalDate today = LocalDate.now();
            int totalProcessedOrders = 0;
            int batchSize = 1000; // 每批处理1000条数据
            
            while (true) {
                // 分批查询已完成且已结算但未统计的订单
                LambdaQueryWrapper<Order> orderQuery = new LambdaQueryWrapper<>();
                orderQuery.eq(Order::getStatus, OrderStatusEnum.COMPLETED.getValue()) // 已完成状态
                         .eq(Order::getProfitSettled, 1) // 已结算
                         .eq(Order::getEarningsSettled, 0) // 未统计收益
                         .lt(Order::getFinishTime, today.atStartOfDay()) // 今天之前完成的订单
                         .orderByAsc(Order::getFinishTime)
                         .last("LIMIT " + batchSize); // 限制查询条数
                
                List<Order> batchOrders = orderService.list(orderQuery);
                
                if (batchOrders.isEmpty()) {
                    log.info("本批次没有需要统计收益的订单，结束处理");
                    break;
                }
                
                log.info("本批次查询到{}个订单需要统计收益", batchOrders.size());
                
                // 按完成日期分组处理订单
                Map<LocalDate, List<Order>> ordersByDate = batchOrders.stream()
                    .collect(Collectors.groupingBy(order -> order.getFinishTime().toLocalDate()));
                
                // 逐日期处理收益统计
                for (Map.Entry<LocalDate, List<Order>> entry : ordersByDate.entrySet()) {
                    LocalDate earningDate = entry.getKey();
                    List<Order> dayOrders = entry.getValue();
                    
                    try {
                        processDateEarnings(earningDate, dayOrders);
                        totalProcessedOrders += dayOrders.size();
                    } catch (Exception e) {
                        log.error("处理日期{}的收益统计失败: {}", earningDate, e.getMessage(), e);
                        // 继续处理下一个日期，不中断整个任务
                    }
                }
                
                // 如果本批次数据少于批次大小，说明已经处理完所有数据
                if (batchOrders.size() < batchSize) {
                    log.info("已处理完所有数据，结束任务");
                    break;
                }
            }
            
            log.info("统一收益结算任务执行完成，共处理{}个订单的收益", totalProcessedOrders);
            
        } catch (Exception e) {
            log.error("统一收益结算任务执行失败", e);
        }
    }
    
    /**
     * 处理指定日期的收益统计
     * 同时处理平台、商户、配送员的收益统计
     * 
     * @param earningDate 收益日期
     * @param dayOrders 该日期的订单列表
     */
    private void processDateEarnings(LocalDate earningDate, List<Order> dayOrders) {
        // 1. 处理平台收益统计
        processPlatformEarnings(earningDate, dayOrders);
        
        // 2. 处理商户收益统计
        processMerchantEarnings(earningDate, dayOrders);
        
        // 3. 处理配送员收益统计
        processDeliveryEarnings(earningDate, dayOrders);
        
        // 4. 标记这些订单的收益已统计
        markOrdersAsSettled(dayOrders);
    }
    
    /**
     * 处理平台收益统计
     */
    private void processPlatformEarnings(LocalDate earningDate, List<Order> dayOrders) {
        // 计算该日期的平台总收益和订单数
        BigDecimal totalEarnings = dayOrders.stream()
            .map(Order::getPlatformProfit)
            .filter(profit -> profit != null)
            .reduce(BigDecimal.ZERO, BigDecimal::add);
        
        int totalOrders = dayOrders.size();
        
        // 检查是否已存在该日期的收益记录
        LambdaQueryWrapper<PlatformDailyEarnings> earningsQuery = new LambdaQueryWrapper<>();
        earningsQuery.eq(PlatformDailyEarnings::getEarningDate, earningDate);
        
        PlatformDailyEarnings existingRecord = platformDailyEarningsService.getOne(earningsQuery);
        
        if (existingRecord != null) {
            // 更新现有记录
            existingRecord.setTotalOrders(existingRecord.getTotalOrders() + totalOrders);
            existingRecord.setTotalEarnings(existingRecord.getTotalEarnings().add(totalEarnings));
            existingRecord.setUpdateTime(LocalDateTime.now());
            platformDailyEarningsService.updateById(existingRecord);
            
            log.info("更新{}的平台收益记录: 订单数+{}, 收益+{}", earningDate, totalOrders, totalEarnings);
        } else {
            // 创建新的收益记录
            PlatformDailyEarnings newRecord = new PlatformDailyEarnings();
            newRecord.setEarningDate(earningDate);
            newRecord.setTotalOrders(totalOrders);
            newRecord.setTotalEarnings(totalEarnings);
            newRecord.setCreateTime(LocalDateTime.now());
            newRecord.setUpdateTime(LocalDateTime.now());
            
            platformDailyEarningsService.save(newRecord);
            
            log.info("创建{}的平台收益记录: 订单数{}, 收益{}", earningDate, totalOrders, totalEarnings);
        }
    }
    
    /**
     * 处理商户收益统计
     */
    private void processMerchantEarnings(LocalDate earningDate, List<Order> dayOrders) {
        // 按商户分组
        Map<Integer, List<Order>> ordersByMerchant = dayOrders.stream()
            .filter(order -> order.getMerchantId() != null && order.getMerchantProfit() != null)
            .collect(Collectors.groupingBy(Order::getMerchantId));
        
        for (Map.Entry<Integer, List<Order>> entry : ordersByMerchant.entrySet()) {
            Integer merchantId = entry.getKey();
            List<Order> merchantOrders = entry.getValue();
            
            // 计算该商户该日期的总收益和订单数
            BigDecimal totalEarnings = merchantOrders.stream()
                .map(Order::getMerchantProfit)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
            
            int totalOrders = merchantOrders.size();
            
            // 检查是否已存在该商户该日期的收益记录
            LambdaQueryWrapper<MerchantDailyEarnings> earningsQuery = new LambdaQueryWrapper<>();
            earningsQuery.eq(MerchantDailyEarnings::getMerchantId, merchantId)
                       .eq(MerchantDailyEarnings::getEarningDate, earningDate);
            
            MerchantDailyEarnings existingRecord = merchantDailyEarningsService.getOne(earningsQuery);
            
            if (existingRecord != null) {
                // 更新现有记录
                existingRecord.setTotalOrders(existingRecord.getTotalOrders() + totalOrders);
                existingRecord.setTotalEarnings(existingRecord.getTotalEarnings().add(totalEarnings));
                existingRecord.setUpdateTime(LocalDateTime.now());
                merchantDailyEarningsService.updateById(existingRecord);
                
                log.info("更新商户{}在{}的收益记录: 订单数+{}, 收益+{}", 
                    merchantId, earningDate, totalOrders, totalEarnings);
            } else {
                // 创建新的收益记录
                MerchantDailyEarnings newRecord = new MerchantDailyEarnings();
                newRecord.setMerchantId(merchantId);
                newRecord.setEarningDate(earningDate);
                newRecord.setTotalOrders(totalOrders);
                newRecord.setTotalEarnings(totalEarnings);
                newRecord.setCreateTime(LocalDateTime.now());
                newRecord.setUpdateTime(LocalDateTime.now());
                
                merchantDailyEarningsService.save(newRecord);
                
                log.info("创建商户{}在{}的收益记录: 订单数{}, 收益{}", 
                    merchantId, earningDate, totalOrders, totalEarnings);
            }
        }
    }
    
    /**
     * 处理配送员收益统计
     */
    private void processDeliveryEarnings(LocalDate earningDate, List<Order> dayOrders) {
        // 按配送员分组
        Map<Integer, List<Order>> ordersByDelivery = dayOrders.stream()
            .filter(order -> order.getDeliveryId() != null && order.getDeliveryProfit() != null)
            .collect(Collectors.groupingBy(Order::getDeliveryId));
        
        for (Map.Entry<Integer, List<Order>> entry : ordersByDelivery.entrySet()) {
            Integer deliveryId = entry.getKey();
            List<Order> deliveryOrders = entry.getValue();
            
            // 计算该配送员该日期的总收益和订单数
            BigDecimal totalEarnings = deliveryOrders.stream()
                .map(Order::getDeliveryProfit)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
            
            int totalOrders = deliveryOrders.size();
            
            // 检查是否已存在该配送员该日期的收益记录
            LambdaQueryWrapper<DeliveryDailyEarnings> earningsQuery = new LambdaQueryWrapper<>();
            earningsQuery.eq(DeliveryDailyEarnings::getDeliveryId, deliveryId)
                       .eq(DeliveryDailyEarnings::getEarningDate, earningDate);
            
            DeliveryDailyEarnings existingRecord = deliveryDailyEarningsService.getOne(earningsQuery);
            
            if (existingRecord != null) {
                // 更新现有记录
                existingRecord.setTotalOrders(existingRecord.getTotalOrders() + totalOrders);
                existingRecord.setTotalEarnings(existingRecord.getTotalEarnings().add(totalEarnings));
                existingRecord.setUpdateTime(LocalDateTime.now());
                deliveryDailyEarningsService.updateById(existingRecord);
                
                log.info("更新配送员{}在{}的收益记录: 订单数+{}, 收益+{}", 
                    deliveryId, earningDate, totalOrders, totalEarnings);
            } else {
                // 创建新的收益记录
                DeliveryDailyEarnings newRecord = new DeliveryDailyEarnings();
                newRecord.setDeliveryId(deliveryId);
                newRecord.setEarningDate(earningDate);
                newRecord.setTotalOrders(totalOrders);
                newRecord.setTotalEarnings(totalEarnings);
                newRecord.setCreateTime(LocalDateTime.now());
                newRecord.setUpdateTime(LocalDateTime.now());
                
                deliveryDailyEarningsService.save(newRecord);
                
                log.info("创建配送员{}在{}的收益记录: 订单数{}, 收益{}", 
                    deliveryId, earningDate, totalOrders, totalEarnings);
            }
        }
    }
    
    /**
     * 标记订单的收益已统计
     */
    private void markOrdersAsSettled(List<Order> dayOrders) {
        List<String> orderIds = dayOrders.stream()
            .map(Order::getId)
            .collect(Collectors.toList());
        
        LambdaUpdateWrapper<Order> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.in(Order::getId, orderIds)
                    .set(Order::getEarningsSettled, 1);
        
        boolean updateSuccess = orderService.update(updateWrapper);
        if (!updateSuccess) {
            throw new RuntimeException("更新订单收益统计状态失败");
        }
        
        log.debug("标记{}个订单的收益已统计", orderIds.size());
    }
}